import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Random;


public class MapNode implements Comparable<MapNode> {
	public int index;
	public int x;
	public int y;
	public double distance = Double.POSITIVE_INFINITY;
	public TerrainType type;
	public int activeQuestIndex = -1;
	public int djur;
	private AI trainer;
	boolean shop = false;
	Inventory inventory = new Inventory();
	public ArrayList<Pet> allPets = new ArrayList<Pet>();
	
	MapNode(int index, int x, int y, TerrainType type, int qi, Player player,ArrayList<Pet> allPets) {
		this.index = index;
		this.x = x;
		this.y = y;
		this.type = type;
		this.activeQuestIndex = qi;
		this.allPets = allPets;
		
		int number = player.seed.nextInt(100);

		
		if(type.equals(TerrainType.City) && number > 0)
			shop = true;
		else if(type.equals(TerrainType.Village) && number > 9)
			shop = true;
		else if(type.equals(TerrainType.Cabin) && number > 39)
			shop = true;
		else if(type.equals(TerrainType.Forest) && number > 59)
			shop = true;
		else if(type.equals(TerrainType.Mountains) && number > 79)
			shop = true;
		else if(player.seed.nextInt(100) > 94)
			shop = true;
		
		createInventory();
	}
	
	public void createInventory(){
		
		if(shop){
			
			Random random = new Random(y);
			
			int itemCount = random.nextInt(25) + 10;
			
			for(int i = 0; i < itemCount; i++){
				int item = random.nextInt(12);
				if(item == 0)
					inventory.addItem(ItemKind.WeakPotion, 1);
				else if(item == 1)
					inventory.addItem(ItemKind.BrassKey, 1);
				else if(item == 2)
					inventory.addItem(ItemKind.DefenseX, 1);
				else if(item == 3)
					inventory.addItem(ItemKind.Lakan, 1);
				else if(item == 4)
					inventory.addItem(ItemKind.Potion, 1);
				else if(item == 5)
					inventory.addItem(ItemKind.SilverKey, 1);
				else if(item == 6)
					inventory.addItem(ItemKind.SizeX, 1);
				else if(item == 7)
					inventory.addItem(ItemKind.SpeedX, 1);
				else if(item == 8)
					inventory.addItem(ItemKind.StrengthX, 1);
				else if(item == 9)
					inventory.addItem(ItemKind.StrongPotion, 1);
				else if(item == 10)
					inventory.addItem(ItemKind.Telescope, 1);
				else if(item == 11)
					inventory.addItem(ItemKind.Tent, 1);
			}
			
		}
		
	}
	
	public Inventory getShopInventory(){
		if(shop)
			return inventory;
		else
			return null;
	}
	
	public AI getTrainer(Player player) {
		if (player.defeatedTrainers.contains(index)) {
			return null;
		} else {
			return trainer.copy();
		}
	}
	
	public void generateTrainer(Random seed) {
		
		ArrayList<String> trainerNames = new ArrayList<String>(Arrays.asList(
			"Karin",
			"Peter",
			"Sara",
			"Åke", 
			"Erik", 
			"Per", 
			"Jan", 
			"Johannes", 
			"Helge", 
			"Stig", 
			"Jim",  
			"Mikael",  
			"Markus", 
			"Thomas", 
			"Ulf", 
			"Olof", 
			"Lars", 
			"Bengt", 
			"Niklas", 
			"Tyke", 
			"Torbjörn", 
			"Johan", 
			"Kristoffer", 
			"Pontus", 
			"William", 
			"Björn", 
			"Lukas", 
			"Love", 
			"Lisa", 
			"Veronika", 
			"Jenny", 
			"Caroline", 
			"Victoria", 
			"Maria", 
			"Felicia", 
			"Amanda", 
			"Britta", 
			"Greta", 
			"Helga", 
			"Linnea", 
			"Ingrid", 
			"Julia"
		));
		
		Random r = seed;
		AI enemy;
		
		if (distance == 1.0) {
			/* TODO: KAN TRIGGAS PÅ MER ÄN EN NOD MED NUVARANDE DISTANSSYSTEM */
			enemy = new AI("BOSS", 100, (Pet) new Sköldpadduru("Paddan", 900), new ArrayList<Pet>(), null);
			enemy.addPetToList(new Bear("boss bear", 677));
			enemy.addPetToList(new Baconator("boss bacon", 900));
			enemy.addPetToList(new Seppo("bosspo", 900));
			enemy.inventory.addItem(ItemKind.WeakPotion, 5);
		} 
		else {
			int randomGold = 100 + (int) (distance*400) + r.nextInt(Math.max((int) (distance*300), 1));
			String name = trainerNames.get(r.nextInt(trainerNames.size()));
			enemy = new AI(name, randomGold, getRandomEncounter(seed).activePet, new ArrayList<Pet>(), null);
			int enemyMaxPets = r.nextInt(3) + 1;
			
			for (int i = 0; i <enemyMaxPets; i++) {
				AI temp = getRandomEncounter(seed);
				enemy.addPetToList(temp.activePet);
				for (ItemKind item : temp.inventory.getItems()) {
					enemy.inventory.addItem(item, temp.inventory.itemCount(item));
				}
			}
		}
		
		trainer = enemy;
	}
	
	public AI getRandomEncounter(Random seed){
		
		ArrayList<AI> randomEnemies = new ArrayList<AI>();
		Random r = seed;
		if (r == null) {
			r = new Random();
		}
		// Math.max eftersom nextInt inte kan ta 0 som argument (sker på första noden...)
		int randomGold = 10 + (int) (distance*300) + r.nextInt(Math.max((int) (distance*200), 1));
		int expUpperBound = (int) ((Math.log10(distance + 0.1) + 1)*900);
		int maxExp = (int) Math.max(distance*900 + 120, 999);
		int expLowerBound = (int) (Math.pow(distance, 2)*900);
		int randomExp = expLowerBound + r.nextInt(clamp(expUpperBound - expLowerBound, 1, maxExp - expLowerBound));
		
		
		switch (type) {
		/* natur */
		case Field:
		case Forest:
			for (Pet pet : allPets){
				if (pet.element == Type.Nature){
					
					Pet newPet = pet.copy(randomExp);
					randomEnemies.add(new AI("Vilding", randomGold, newPet, new ArrayList<Pet>(), null));	
				}
				if (pet.element == Type.Energy && r.nextInt(100) < 50){
					
					Pet newPet = pet.copy(randomExp);
					randomEnemies.add(new AI("Vilding", randomGold, newPet, new ArrayList<Pet>(), null));
				}
				if (pet.element == Type.Water && r.nextInt(100) < 20){
					
					Pet newPet = pet.copy(randomExp);
					randomEnemies.add(new AI("Vilding", randomGold, newPet, new ArrayList<Pet>(), null));
				}
				if (pet.element == Type.Fire && r.nextInt(100) < 40){
					
					Pet newPet = pet.copy(randomExp);
					randomEnemies.add(new AI("Vilding", randomGold, newPet, new ArrayList<Pet>(), null));
				}
			}
			break;
			
		/* energi */
		case City:
		case Mountains:
			for (Pet pet : allPets){
				if (pet.element == Type.Energy){
					
					Pet newPet = pet.copy(randomExp);
					randomEnemies.add(new AI("Vilding", randomGold, newPet, new ArrayList<Pet>(), null));	
				}
				if (pet.element == Type.Fire && r.nextInt(100) < 75){
					
					Pet newPet = pet.copy(randomExp);
					randomEnemies.add(new AI("Vilding", randomGold, newPet, new ArrayList<Pet>(), null));
				}
				if (pet.element == Type.Water && r.nextInt(100) < 30){
					
					Pet newPet = pet.copy(randomExp);
					randomEnemies.add(new AI("Vilding", randomGold, newPet, new ArrayList<Pet>(), null));
				}
			}
			break;
			
		/* vatten */
		case Swamp:
		case Lake:
		case Village:
			for (Pet pet : allPets){
				if (pet.element == Type.Water){
					
					Pet newPet = pet.copy(randomExp);
					randomEnemies.add(new AI("Vilding", randomGold, newPet, new ArrayList<Pet>(), null));	
				}
				if (pet.element == Type.Nature && r.nextInt(100) < 25){
					
					Pet newPet = pet.copy(randomExp);
					randomEnemies.add(new AI("Vilding", randomGold, newPet, new ArrayList<Pet>(), null));
				}
			}
			
			break;
			
		/* eld */
		case Sand:
		case Cabin:
			for (Pet pet : allPets){
				if (pet.element == Type.Fire){
					
					Pet newPet = pet.copy(randomExp);
					randomEnemies.add(new AI("Vilding", randomGold, newPet, new ArrayList<Pet>(), null));	
				}
				if (pet.element == Type.Energy && r.nextInt(100) < 25){
					
					Pet newPet = pet.copy(randomExp);
					randomEnemies.add(new AI("Vilding", randomGold, newPet, new ArrayList<Pet>(), null));
				}
			}
			
			break;
			
		default:
			for (Pet pet : allPets){
				Pet newPet = pet.copy(randomExp);
				randomEnemies.add(new AI("Vilding", randomGold, newPet, new ArrayList<Pet>(), null));
			}
		}		

		AI encounter = randomEnemies.get(r.nextInt(randomEnemies.size()));
		ItemKind item = lootList(randomExp).get(r.nextInt(lootList(randomExp).size()));
		encounter.inventory.addItem(item, r.nextInt(4));
		
		
		return encounter;
	}
	
	private ArrayList<ItemKind> lootList(int randomExp){
		
        ArrayList<ItemKind> items = new ArrayList<ItemKind>();
        
        if(randomExp < 200){
        	
			items.add(ItemKind.WeakPotion);
			items.add(ItemKind.WeakPotion);
			items.add(ItemKind.WeakPotion);
			items.add(ItemKind.WeakPotion);
			items.add(ItemKind.Potion);
			items.add(ItemKind.Potion);
			items.add(ItemKind.BrassKey);
			items.add(ItemKind.BrassKey);
			items.add(ItemKind.SilverKey);
			items.add(ItemKind.Lakan);
			items.add(ItemKind.SpeedX);
			items.add(ItemKind.DefenseX);
			items.add(ItemKind.SizeX);
			items.add(ItemKind.StrengthX);
			items.add(ItemKind.Telescope);
			items.add(ItemKind.Tent);
       }

       else if(randomExp < 500){

           items.add(ItemKind.WeakPotion);
           items.add(ItemKind.WeakPotion);
           items.add(ItemKind.Potion);
           items.add(ItemKind.Potion);
           items.add(ItemKind.StrongPotion);
           items.add(ItemKind.BrassKey);
           items.add(ItemKind.SilverKey);
           items.add(ItemKind.SilverKey);
           items.add(ItemKind.Lakan);
           items.add(ItemKind.SpeedX);
           items.add(ItemKind.DefenseX);
           items.add(ItemKind.SizeMX);
           items.add(ItemKind.StrengthX);
           items.add(ItemKind.Telescope);
           items.add(ItemKind.Tent);   

       }

       else if (randomExp < 800){

           items.add(ItemKind.Potion);
           items.add(ItemKind.Potion);
           items.add(ItemKind.StrongPotion);
           items.add(ItemKind.StrongPotion);
           items.add(ItemKind.StrongPotion);
           items.add(ItemKind.SilverKey);
           items.add(ItemKind.SilverKey);
           items.add(ItemKind.GoldenKey);
           items.add(ItemKind.Lakan);
           items.add(ItemKind.SpeedX);
           items.add(ItemKind.DefenseX);
           items.add(ItemKind.SizeMX);
           items.add(ItemKind.StrengthMX);
           items.add(ItemKind.Telescope);
           items.add(ItemKind.Tent);

       }

       else {

           items.add(ItemKind.MegaPotion);
           items.add(ItemKind.MegaPotion);
           items.add(ItemKind.StrongPotion);
           items.add(ItemKind.StrongPotion);
           items.add(ItemKind.StrongPotion);
           items.add(ItemKind.DiamondKey);
           items.add(ItemKind.GoldenKey);
           items.add(ItemKind.GoldenKey);
           items.add(ItemKind.Lakan);
           items.add(ItemKind.SpeedMX);
           items.add(ItemKind.DefenseMX);
           items.add(ItemKind.SizeMX);
           items.add(ItemKind.StrengthMX);
           items.add(ItemKind.Telescope);
           items.add(ItemKind.Tent);

       }
        
        return items;
		
	}

    @Override
    public int compareTo(MapNode n) {
    	if (distance < n.distance) {
    		return -1;
    	} else if (distance == n.distance) {
    		return 0;
    	} else {
    		return 1;
    	}
    }
    

    int clamp(int i, int min, int max) {
    	return Math.max(min, Math.min(max, i));
    }
    
}
	

